package com.goodluck.goodluckim.network;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;

import com.goodluck.goodluckim.BuildConfig;
import com.goodluck.goodluckim.sdk.LogUtils;
import com.goodluck.goodluckim.sdk.constant.MsgActions;
import com.goodluck.goodluckim.ui.IMApplication;
import com.goodluck.goodluckim.utils.GsonUtil;

import org.json.JSONException;
import org.json.JSONObject;

import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * Created by wyj on 2018/9/3.
 */
public class WebSocketClient {
    private static final String getUrl(String token) {
        return System.getProperty("url", "ws://27.154.231.142:8000/websocket?token=" + token + "&platform=a&appVer=" + BuildConfig.VERSION_NAME);
    }

    private Channel channel;

    /**
     * 是否连接成功
     */
    private boolean isConnect = false;
    /**
     * 判断服务器是否有响应
     */
    private boolean isReponse;

    /**
     * 是否正在连接
     */
    private boolean isStartConnect = false;
    private Object mLock = new Object();
    private NioEventLoopGroup group;
    private WebSocketListener listener;

    private Handler mTimeOutHandler;

    public void setListener(WebSocketListener listener) {
        this.listener = listener;
    }

    private static class InstanceHolder {
        static WebSocketClient netWorkClient = new WebSocketClient();
    }

    public static WebSocketClient getInstance() {
        return WebSocketClient.InstanceHolder.netWorkClient;
    }

    private ThreadPoolExecutor connectThreadPoolExecutor;

    private WebSocketClient() {

    }

    /**
     * 连接服务器
     */
    public void connect() {
        if (isStartConnect || isConnect) {
            return;
        }
        synchronized (mLock) {
            if (connectThreadPoolExecutor == null) {
                connectThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
            }
            isStartConnect = true;
            connectThreadPoolExecutor.execute(connectRunnable);
        }
    }

    private Runnable connectRunnable = new Runnable() {
        @Override
        public void run() {
            if (isConnect) {
                return;
            }
            disconnect();
            group = new NioEventLoopGroup();
            try {
                URI uri = new URI(getUrl(IMApplication.getApplication().getSharedPreferences("user_info", Context.MODE_PRIVATE).getString("token", "")));
                LogUtils.printe("uri = " + uri.toString());
                WebSocketClientHandler handler = new WebSocketClientHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, false, new DefaultHttpHeaders()), listener);
                Bootstrap bootstrap = new Bootstrap().group(group)
                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                        .channel(NioSocketChannel.class)
                        .handler(new WebSocketInitializer(handler));
                channel = bootstrap.connect(uri.getHost(), uri.getPort()).sync().channel();
                handler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        isStartConnect = false;
                        if (future.isSuccess()) {
                            isConnect = true;
                            stopConnect();
                        } else {
                            isConnect = false;
                        }
                    }
                }).sync();
            } catch (Exception e) {
                e.printStackTrace();
                isStartConnect = false;
                listener.onServiceStatusConnectChanged(WebSocketListener.STATUS_CONNECT_ERROR);
            }
        }
    };

    /**
     * 断开连接
     */
    public void disconnect() {
        synchronized (mLock) {
            stopCheckTimeout();
            isConnect = false;
            isStartConnect = false;
            if (group != null) {
                group.shutdownGracefully();
            }
        }
    }

    /**
     * 停止连接
     */
    public void stopConnect() {
        if (connectThreadPoolExecutor != null && !connectThreadPoolExecutor.isShutdown()) {
            connectThreadPoolExecutor.shutdownNow();
            connectThreadPoolExecutor = null;
        }
    }

    /**
     * 发送消息
     *
     * @param message
     */
    public ChannelFuture request(Object message) {
        if (channel == null || !isConnect) {
            return failChannelFuture;
        }
        try {
            JSONObject jsonObject = new JSONObject();
            String action = MsgActions.getActionByClass(message.getClass());
            jsonObject.put("action", action);
            jsonObject.put("data", GsonUtil.getInstance().toJson(message));
            WebSocketFrame webSocketFrame = new TextWebSocketFrame(jsonObject.toString());
            startCheckTimeout();
            return channel.writeAndFlush(webSocketFrame);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return failChannelFuture;
    }

    /**
     * 发送消息
     *
     * @param data
     */
    public ChannelFuture request(String data) {
        if (channel == null || !isConnect) {
            return failChannelFuture;
        }
        WebSocketFrame webSocketFrame = new TextWebSocketFrame(data);
        startCheckTimeout();
        return channel.writeAndFlush(webSocketFrame);
    }

    /**
     * 失败的回调
     */
    private ChannelFuture failChannelFuture = new ChannelFuture() {
        @Override
        public Channel channel() {
            return null;
        }

        @Override
        public boolean isSuccess() {
            return false;
        }

        @Override
        public boolean isCancellable() {
            return false;
        }

        @Override
        public Throwable cause() {
            return null;
        }

        @Override
        public ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> genericFutureListener) {
            try {
                ((ChannelFutureListener) genericFutureListener).operationComplete(failChannelFuture);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... genericFutureListeners) {
            return null;
        }

        @Override
        public ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> genericFutureListener) {
            return null;
        }

        @Override
        public ChannelFuture removeListeners(GenericFutureListener<? extends Future<? super Void>>... genericFutureListeners) {
            return null;
        }

        @Override
        public ChannelFuture sync() throws InterruptedException {
            return null;
        }

        @Override
        public ChannelFuture syncUninterruptibly() {
            return null;
        }

        @Override
        public ChannelFuture await() throws InterruptedException {
            return null;
        }

        @Override
        public ChannelFuture awaitUninterruptibly() {
            return null;
        }

        @Override
        public boolean await(long l, TimeUnit timeUnit) throws InterruptedException {
            return false;
        }

        @Override
        public boolean await(long l) throws InterruptedException {
            return false;
        }

        @Override
        public boolean awaitUninterruptibly(long l, TimeUnit timeUnit) {
            return false;
        }

        @Override
        public boolean awaitUninterruptibly(long l) {
            return false;
        }

        @Override
        public Void getNow() {
            return null;
        }

        @Override
        public boolean cancel(boolean b) {
            return false;
        }

        @Override
        public boolean isCancelled() {
            return false;
        }

        @Override
        public boolean isDone() {
            return false;
        }

        @Override
        public Void get() throws InterruptedException, ExecutionException {
            return null;
        }

        @Override
        public Void get(long timeout, @NonNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            return null;
        }

        @Override
        public boolean isVoid() {
            return false;
        }
    };

    /**
     * 开启断线检测
     */
    public void startCheckTimeout() {
        isReponse = false;
        if (mTimeOutHandler == null) {
            mTimeOutHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    stopCheckTimeout();
                    /**
                     * 服务器无响应,自动重连
                     */
                    if (!isReponse) {
                        connect();
                    }
                    isReponse = false;
                }
            };
        }
        mTimeOutHandler.sendEmptyMessageDelayed(1, 5000);
    }

    /**
     * 停止检测断线重连（主动断开或者被踢下线情况）
     */
    public void stopCheckTimeout() {
        if (mTimeOutHandler != null) {
            mTimeOutHandler.removeCallbacksAndMessages(null);
        }
    }

    /**
     * 服务器已响应或者已经不需要再做连接检测情况下主动设置状态
     */
    public void reponse() {
        isReponse = true;
        stopCheckTimeout();
    }

    /**
     * 是否连接
     *
     * @return
     */
    public boolean isConnect() {
        return isConnect;
    }
}
